package im.starchat.chat.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import im.starchat.chat.R;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;

public class DanMuView extends View{

    boolean mHaveInit = false;
    Queue<UserInfo> mTextQueue = new LinkedList<>(); // 待展示的数据
    List<List<ToastModel>> mLineList = new ArrayList<>();// 所有的行
    Paint mDescPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 画描述的画笔
//    int[] mColors = new int[]{
//            Color.RED, Color.GREEN, Color.BLUE};
    int mElementGap; // 单行内，每两个元素之间的距离
    int mLineGap;// 行与行之间的距离
    Paint.FontMetricsInt mDescFontMetrics;// 画描述的画笔的 FontMetricx
    int mDescFontMetricxIntHeight; // 画描述的画笔的 Fontmetricx 的高度
    boolean mGravityBottom; // 优先展示上面的行，还是下面的行
    int mStep ; // 每次 invalidate 时的移动间距
    Random mRandom;
    int mHeadRadus; // 头像的半径
    BitmapShader mDefaultHeadBitmapShader; // 默认的头像 Shader
    Paint mNamePaint; // 画名字的画笔
    Paint.FontMetricsInt mNameFontMetricsInt; // 画名字的画笔的 FontMetricx
    int mNameFontHeight; // 画名字的画笔的高度
    int mHeadNameGap; // 头像、文字之间的间距
    int mDescExtraSpace;// 描述尾部额外的距离
    int mDescHeight;// 描述的整体高度(包含文字和背景)
    int mNameTextGap;// 姓名 和 描述之间的间距
    int mMaxWorld;// 姓名、描述的最大字数
    Paint mWhiteStrokePaint;// 用来画头像白色描边的画笔

    public DanMuView(Context context) {
        super(context);
        init();
    }

    public DanMuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DanMuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        if (mHaveInit) {
            return;
        }
        mHaveInit = true;
        mWhiteStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mWhiteStrokePaint.setColor(Color.WHITE);
        mWhiteStrokePaint.setStyle(Paint.Style.STROKE);
        mWhiteStrokePaint.setStrokeWidth(getResources().getDisplayMetrics().density);
        mMaxWorld = 50;
        mDescPaint.setTextSize(px2dp(15));
        mDescPaint.setColor(getResources().getColor(R.color.white));
        mElementGap = px2dp(20); //(int) (getResources().getDisplayMetrics().density * 20f);
        mLineGap = px2dp(10);//(int) (getResources().getDisplayMetrics().density * 10f);
        mDescFontMetrics = mDescPaint.getFontMetricsInt();
        mDescFontMetricxIntHeight = mDescFontMetrics.descent - mDescFontMetrics.ascent;
        mLineList.add(new ArrayList<ToastModel>());
        mLineList.add(new ArrayList<ToastModel>());
        mStep = px2dp(2);//(int) (getResources().getDisplayMetrics().density * 2f);
        mRandom = new Random();

        mNamePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mNamePaint.setTextSize(px2dp(12));
        mNamePaint.setColor(getResources().getColor(R.color.color_text_6));
        mNameFontMetricsInt = mNamePaint.getFontMetricsInt();
        mNameFontHeight = mNameFontMetricsInt.descent - mNameFontMetricsInt.ascent;

        mHeadRadus = px2dp(18);//(int) (getResources().getDisplayMetrics().density * 18);
        Matrix headMatrix = new Matrix();
        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.follows_head01);
        Bitmap bmp = bitmapDrawable.getBitmap();
        float sx = (mHeadRadus * 2.0f) / bmp.getWidth();
        float sy = (mHeadRadus * 2.0f) / bmp.getHeight();
        headMatrix.postScale(sx, sy, 0.5f, 0.5f);
        bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), headMatrix, false);
        mDefaultHeadBitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP,
                Shader.TileMode.CLAMP);

        mHeadNameGap = px2dp(3);//(int) (getResources().getDisplayMetrics().density * 3);
        mDescExtraSpace = px2dp(20);//(int) (getResources().getDisplayMetrics().density * 20);

        mDescHeight = px2dp(22);//(int) (getResources().getDisplayMetrics().density * 22);
        mNameTextGap = px2dp(2);//(int) (getResources().getDisplayMetrics().density * 2);

    }

    public void toastText(long id, String name, String desc, String headUrl) {
        mTextQueue.add(new UserInfo(id, name, desc, headUrl));
        recollectTexts(getMeasuredWidth() * 2);
    }

    private void recollectTexts(int maxWidth) {
        if (maxWidth < 0) {
            return;
        }
        int oldSize = 0;
        for (List<ToastModel> line : mLineList) {
            if (line.size() == 0) {
                continue;
            }
            oldSize++;
            // 看是否需要删除头元素
            Iterator<ToastModel> iterator = line.iterator();
            while (iterator.hasNext()) {
                ToastModel model = iterator.next();
                if (model.mBounds.right + (line.size() == 1 ? 0 : mElementGap) <= 0) {
                    iterator.remove();
                }
            }
        }
        int start = mGravityBottom ? mLineList.size() - 1 : 0;
        int end = mGravityBottom ? 0 : mLineList.size() - 1;
        for (int i = start; i <= end; i = mGravityBottom ? --i : ++i) {
            List<ToastModel> line = mLineList.get(i);
            int usedSpace = 0;
            for (ToastModel model : line) {
                usedSpace += (model.mBounds.width());
            }
            usedSpace += (line.size() - 1) * mElementGap;
            if (usedSpace < maxWidth && mTextQueue.size() > 0) {
                ToastModel model = new ToastModel(getContext());
                model.line = i;
                UserInfo userInfo = mTextQueue.poll();
                model.set(userInfo.desc, userInfo.name, userInfo.headUrl, userInfo.id);
//                Drawable drawable = getContext().getResources()
//                        .getDrawable(mDrawableRes[mRandom.nextInt(10) % mDrawableRes.length]);
//                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
//                model.mIconDrawale = drawable;
//                model.measureWidth = (int) + (mPaint.measureText(model.text) + 1);
//                if (model.mIconDrawale != null) {
//                    model.measureWidth += (mIconTextGap + model.mIconDrawale.getIntrinsicWidth());
//                }
                ToastModel lastModel = line.size() > 0 ? line.get(line.size() - 1) : null;
                int lineHeight = model.line * (mHeadRadus * 2 + mLineGap);
                if (lastModel == null) {
                    model.measure(maxWidth / 2, lineHeight);
                    //model.colorStyle = mRandom.nextInt(10) % mColors.length;
                } else {
                    int left = Math.max(lastModel.mBounds.right + mElementGap, maxWidth / 2);
                    model.measure(left, lineHeight);
                   // model.colorStyle = (lastModel.colorStyle + mRandom.nextInt(10)) % mColors.length;
                }
                line.add(model);
            }
        }
        int newSize = 0;
        for (List<ToastModel> line : mLineList) {
            if (line.size() == 0) {
                continue;
            }
            newSize++;
        }
        if (newSize != oldSize) {
            requestLayout();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = mHeadRadus * 2;
        int validLine = 0;
        for (List<ToastModel> line : mLineList) {
            if (line.size() > 0) {
                validLine++;
            }
        }
        if (validLine == 0) {
            height = 0;
        } else if (validLine > 1) {
            height = height * validLine + (validLine - 1) * mLineGap;
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        boolean canMeasure = false;
        boolean needInvalidate = false;
        for (List<ToastModel> line : mLineList) {
            if (line.size() > 0) {
                needInvalidate = true;
            }
            for (ToastModel model : line) {
                model.draw(canvas);
/*

                int lineTop = model.line * (mLineHeight + mLineGap);
                int y = lineTop - mFontMetrics.ascent;
                mPaint.setColor(mColors[model.colorStyle]);
                int x = model.x;
                if (model.mIconDrawale != null) {
                    canvas.save();
                    canvas.translate(x, lineTop
                            + (mLineHeight - model.mIconDrawale.getIntrinsicHeight()) / 2);
                    model.mIconDrawale.draw(canvas);
                    canvas.restore();
                    x += (model.mIconDrawale.getIntrinsicWidth() + mIconTextGap);
                }
                canvas.drawText(model.text, x, y, mPaint);
                */
                model.mBounds.offset(-mStep, 0);
                if (model.mBounds.right
                        + (line.size() == 1 ? 0 : mElementGap) <= 0) {
                    canMeasure = true;
                }
            }
        }
        if (canMeasure && mTextQueue.size() > 0) {
            recollectTexts(getMeasuredWidth() * 2);
            postInvalidateDelayed(5);
        } else if (needInvalidate) {
            postInvalidateDelayed(5);
        }
    }

    class ToastModel implements Target{
        Context mContext;
        Paint mBitmapPaint;
        Rect mBounds;
        int mWidth;
        Drawable mTextBgDrawable;

        ToastModel(Context context) {
            mContext = context;
            mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mBitmapPaint.setShader(mDefaultHeadBitmapShader);
            mBounds = new Rect();
            mTextBgDrawable = getResources().getDrawable(R.drawable.danmu_bg);

        }

        void draw(Canvas canvas) {
            canvas.save();
            canvas.translate(mBounds.left, mBounds.top);
            int textLeft = mHeadRadus * 2 + mHeadNameGap;

            mTextBgDrawable.draw(canvas);
            int textTop = mHeadRadus * 2 - mDescHeight;
            canvas.drawText(mText, textLeft,
                    textTop + (mDescHeight - mDescFontMetricxIntHeight)/2 - mDescFontMetrics.ascent, mDescPaint);
            canvas.drawText(mName, textLeft, textTop - mNameTextGap - mNameFontMetricsInt.descent,
                    mNamePaint);
            canvas.drawCircle(mHeadRadus, mHeadRadus, mHeadRadus, mBitmapPaint);
            canvas.drawCircle(mHeadRadus, mHeadRadus, mHeadRadus - 1, mWhiteStrokePaint);
            canvas.restore();
        }

        int getWidth(){
            return mWidth;
        }

        int getHeight() {
            return mHeadRadus * 2;
        }

        public void set(String text, String name, String headUrl, long id) {
            if (text == null || text.trim().length() == 0) {
                text = "";
            }
            if (name == null || name.trim().length() == 0) {
                name = "";
            }
            if (text.length() > mMaxWorld) {
                text = text.substring(0, mMaxWorld) + "...";
            }
            if (name.length() > mMaxWorld) {
                name = name.substring(0, mMaxWorld) + "...";
            }
            mText = text;
            mName = name;
            mHeadUrl = headUrl;
            mId = id;
            if (headUrl != null || headUrl.length() > 0) {
                Picasso.with(mContext).load(headUrl).into(this);
            }
        }

        public void measure(int left, int top) {
//            mBounds.set();

            int nameWidth = (int) (mNamePaint.measureText(mName) + 0.5f);
            int textWidth = (int) (mDescPaint.measureText(mText) + 0.5f);
            textWidth += mDescExtraSpace;
            mWidth = mHeadRadus * 2 + mHeadNameGap + Math.max(nameWidth, textWidth);
            mBounds.set(left, top, left + mWidth, top + mHeadRadus * 2);
            mTextBgDrawable.setBounds(mHeadRadus, mHeadRadus * 2 - mDescHeight,
                    mHeadRadus * 2 + mHeadNameGap  + textWidth, mHeadRadus * 2);
        }

        String mName;
        long mId;
        String mText;
        int colorStyle;
        int line;
        int measureWidth;
//        int x;
        String mHeadUrl;

        @Override
        public void onBitmapLoaded(Bitmap bmp, Picasso.LoadedFrom from) {
            Matrix headMatrix = new Matrix();
            float sx =  (mHeadRadus * 2.0f) / bmp.getWidth();
            float sy =  (mHeadRadus * 2.0f) / bmp.getHeight();
            headMatrix.postScale(sx, sy, 0.5f, 0.5f);
            bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), headMatrix, false);
            BitmapShader bitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP,
                    Shader.TileMode.CLAMP);
            mBitmapPaint.setShader(bitmapShader);
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {}

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {}


    }

    static class UserInfo {
        long id;
        String name;
        String desc;
        String headUrl;

        public UserInfo(){}

        public UserInfo(long id, String name, String desc, String headUrl) {
            this.id = id;
            this.name = name;
            this.desc = desc;
            this.headUrl = headUrl;
        }
    }

    private int px2dp (int dp) {
        return (int) (getContext().getResources().getDisplayMetrics().density * dp + 0.5f);
    }

}
